﻿#include "IniFile.h"

#include "detail.h" 

 
#pragma warning(push) 
#pragma warning( disable : 4290 )
#pragma warning( disable : 4996 )


namespace ksini
{

    


//
//   Ini file
//

//===================================================
bool  IniFile::searchSection(  int& index, const std::string& sSection) const
{
   index = -1;
	if(m_vector_sections.size() == 0) return false;
	
	for(int c=0; c<(int)m_vector_sections.size() ; c++)
	{
		const  IniSection& sect = m_vector_sections.at(c);
		if(sect.getSectionName() == sSection) 
		{
			   // found !
			   index = c;
			   return true;
		}
		
	}

	return false;	
}
 

//===================================================
bool  IniFile::isSectionExists(const std::string& sSection) const 
{
	int index = -1;
	if( !searchSection(index, sSection ) )
	{
		  // not found !
		 return false;
	}
	
	return true;
}

//=================================================
IniSection*  IniFile::addSection(const std::string& sSection, 
								 const std::string& commentLine) 
{
	IniSection sect(sSection, commentLine);
	m_vector_sections.push_back(sect);

	 IniSection* psect =
	&m_vector_sections.at( m_vector_sections.size() - 1 );


  return 	psect;
}

//=======================================================
 IniSection&    IniFile::getSectionByIndex(const int index) throw(std::runtime_error&)
{
	if(index >=(int)m_vector_sections.size() )
	{
		// error
		throw std::runtime_error("invalid index");

	}
	
	 IniSection& res = m_vector_sections.at(index);
	return res;
}

//===========================================================
const  IniSection&   IniFile::getSectionByIndex( const int index   ) const throw(std::runtime_error&)
{
	if(index >=(int)m_vector_sections.size() )
	{
		throw std::runtime_error("invalid index");
	}
	
	const  IniSection& res = m_vector_sections.at(index);

	return res;	
}


//=================================================================
bool  IniFile::parseKeyValueString(  std::string& s_OutKey, 
						   std::string& s_OutValue,
						 const std::string& src ) const 
{
	s_OutKey = ""; 
	s_OutValue = "";

	const size_t SLEN = src.length();
	if(SLEN == 0) return false;

	int nDivPos =  -1; // position '='

	// read m_key
	//
	for(size_t c=0; c<SLEN; c++)
	{
		if(  (src[c] == '\t') || (src[c] == ' ') || (src[c] == '\r') )
			continue;

		if( src[c] == '=' )
		{
			// found 
			nDivPos = (int)c;
			break;
		}


		s_OutKey += src[c];
	}

	// read m_value
	//
	for(size_t c=nDivPos+1; c<SLEN; c++)
	{
		s_OutValue += src[c];
	}
 
	// add code correct m_value .... 

	return true;
}

//=================================================================
bool  IniFile::read_from_string(const std::string& src) 
{
	clear_all();

	std::vector<std::string>  vs; 
 
	detail::split_lines (vs, src );

	if(vs.size() == 0) 
	{
		return false;
	}
 


	std::string sCurr_key;
	std::string sCurr_value;
	std::string sCurr_comment;


 
	 IniSection* pCurrSect = NULL;

	 bool bresult = true;

	for(int c=0; c<(int)vs.size(); c++)
	{
		  std::string current = vs.at(c);

		  sCurr_key = "";
		  sCurr_value = "";

		  detail::trim_string(current);


		  if(current.length() == 0)
		  {
			  sCurr_comment = "";
			  continue;
		  }

		  // comment
		  {
			  std::string temp_comment;
			  if( detail::is_comment(temp_comment, current) )
			  {
				  sCurr_comment = temp_comment;
				  continue;
			  }
		  }


 

 
			{
				std::string section_name;
				if(detail::is_section_name(section_name, current))
				{
 
					pCurrSect = addSection(section_name, sCurr_comment);
					continue;
				}
			}
 
		

		if( !parseKeyValueString(sCurr_key, sCurr_value,  current) )
		{
			// error 
			return false;		   
		}

	    // add m_key m_value
	    if(pCurrSect)
		{
		    if( !pCurrSect->addKeyValue(sCurr_key, sCurr_value, sCurr_comment) ) 
		    {
			   // error add m_key m_value
			   return false;
			   
			}


		}
	    else
		{
		   // section none
		   return false;
		   
		}





	} // for each vector



	return true;
}

//=================================================================
bool  IniFile::write_to_string(  std::string& dest) const
{
	dest = "";

	std::string str_sect_comment_line;
	for(int c=0; c<(int)m_vector_sections.size(); c++) 
	{
		const IniSection& sect =  m_vector_sections.at(c);

		str_sect_comment_line = sect.getCommentLine();

		if(str_sect_comment_line.length()!=0)
		{
			dest += "; ";
			dest += str_sect_comment_line;
			dest += "\n";
		}


		  dest += "[";
		  dest += sect.getSectionName();
		  dest += "]\n";

		for(int j=0; j<sect.getNumKeys(); j++)
		{
			const IniKeyValue& kval = sect.getKeyValueByIndex(j);
			if(kval.get_comment_line().length())
			{
				dest += "; ";
				dest += kval.get_comment_line();
				dest += "\n";
			}
			dest += kval.get_key();
			dest += "=";
			dest += kval.get_value();
			dest += "\n";
			  
		} // for j
		
		  dest += "\n";
		
	} // for c
 
	return  true;
}

//===========================================================
bool  IniFile::load_from_file(const std::string& fname)  
{
	
	std::string str ;
	str.reserve(2048);


	try
	{
		detail::loadStrFromFile(str, fname);
	}
	catch( ... )
	{
		//throw e;
		std::cerr << "load file error" << std::endl;
	    return false;
	}
 
	return  read_from_string(str);	
}

//===========================================================
bool  IniFile::save_to_file(const std::string& fname) const 
{
	std::string str = "";

	try
	{
		write_to_string(str);
	}
	catch(std::runtime_error e)
	{
		throw std::runtime_error( e.what() );
	}

	try
	{
	    detail::saveStrToFile(str, fname);
		return false;
	}
	catch(std::runtime_error e)
	{
		throw e;
	}

	return true;	
}

namespace detail
{

//#ifdef WIN32
#if 0

std::string convertWstringToAstringWinapi(const  std::wstring& src  )	 throw (std::runtime_error)
{
	if( src.length() < 1024 )
	{
		char temp[1048];
		temp[0] = '\0';

		int winapires = WideCharToMultiByte( CP_ACP, 0, src.c_str(), -1, temp, 1048, NULL, NULL );
		if(winapires==0)
		{
			throw std::runtime_error("Error call WideCharToMultiByte");
		}		


		return std::string(temp);
	}


	char* temp = new char [ src.length() + 16 ];

	int winapires = WideCharToMultiByte( CP_ACP, 0, src.c_str(), -1, temp, (int)(src.length() + 8), NULL, NULL );
	if(winapires == 0)
	{
		delete [] temp;
		throw std::runtime_error("Error call WideCharToMultiByte");
	}

	std::string res(temp);
	delete [] temp;
	return res;
}
   /*
std::wstring convertAstringToWstringWinapi( const std::string& src ) 	 throw (std::runtime_error) 
{
	if( src.length() < 1024 )
	{
		wchar_t temp[1048];
		temp[0] = L'\0';

		int winapires = MultiByteToWideChar( CP_ACP, 0, src.c_str(), (int)(src.length()+1), temp,  1048 );
		if(winapires==0)
		{
			throw std::runtime_error("Error call MultiByteToWideChar");
		}

		return std::wstring(temp);
	}

	wchar_t* temp = new wchar_t [ src.length() + 32 ];

	int winapires = MultiByteToWideChar( CP_ACP, 0, src.c_str(), (int)(1 + (int)src.length() ),  temp,  (int)(src.length() + 16)  );
	if(winapires==0)
	{
		delete [] temp;
		throw std::runtime_error("Error call MultiByteToWideChar");
	}

	std::wstring res (temp);
	delete [] temp;
	return res;
}
*/

#endif // win32  

}

bool IniFile::load_from_file(const std::wstring& fname)
{
	assert(false); return false;
	//std::string temp = detail::convertWstringToAstringWinapi(fname);
	//return load_from_file(temp);
}

//! \brief  записать в ini-файл (wstring).
bool IniFile::save_to_file(const std::wstring& fname) const
{
	assert(false); return false;
	//std::string temp = detail::convertWstringToAstringWinapi(fname);
	//return save_to_file(temp);
	// 
}


void IniFile::set_value(const std::string& sSection, 
			   const std::string& sKey, 
			   const std::string& value, const char* comment ) throw  (std::runtime_error)
{
	IniKeyValue kval (sKey, value, comment);	

	int indexSect = -1;
	if( !searchSection(indexSect, sSection) ) 
	{
		// section not found

		// create section
		IniSection* pSect =    addSection(sSection);
		if( !pSect->addKeyValue(sKey, value, comment) )
		{
			throw (std::runtime_error("Error add m_key") );
		}

	}
	else
	{
		// section FOUND !
		IniSection& sect_ref =  getSectionByIndex(indexSect);
		if( !sect_ref.addKeyValue(sKey, value, comment) ) 
		{
			throw (std::runtime_error("Error add m_key m_value") );
		} 

	}

}

std::string IniFile::get_value(const std::string& sSection, 
					  const std::string& sKey, 
					  const std::string& default_value) throw  (std::runtime_error)
{

	int index = -1;
	if( !searchSection(index, sSection ) )
	{
		// not found  // create section
		IniSection* sect = addSection(sSection);
		if(!sect)
		{
			throw  std::runtime_error("error add section");
		}

		IniKeyValue kval(sKey, default_value, "");
		if(!sect->addKeyValue(kval))
		{
			throw std::runtime_error("Error add m_key");
		}

		return std::string(default_value);
	}

	IniSection& sect = getSectionByIndex(index);

	index = -1;
	if(!sect.searchKey(index, sKey)  )
	{
		// not found. add new m_key
		IniKeyValue  kval(sKey, default_value, "");
		if( !sect.addKeyValue(kval) )
		{
			throw std::runtime_error("Error add m_key");
		}

		return std::string(default_value);

	}

	const IniKeyValue& kval = sect.getKeyValueByIndex(index);

	return kval.get_value();
}




void IniFile::set_bool( const std::string& sSection, 
			  const std::string& sKey, const bool value, const char* comment  )
{
   int ival = 0;
   if(value)
   {
	   ival = 1;
   }
   else
   {
	   ival = 0;
   }

   set_int(sSection, sKey, ival, comment);

}


bool IniFile::get_bool(const std::string& sSection, 
					  const std::string& sKey, 
					  bool default_value)
{
	int int_val = 0;

	if(default_value) int_val = 1;
	else int_val = false;

	if( get_int(sSection, sKey, int_val) != 0 )
	{
		return  true;
	}
 
	return  false;
}


void IniFile::set_int( const std::string& sSection, 
			 const std::string& sKey, const int value , const char* comment )
{
	std::ostringstream ss;
	ss << value;
	set_value( sSection, sKey, ss.str(), comment );
}


int IniFile::get_int(const std::string& sSection, 
			 const std::string& sKey, 
			 const int default_value)
{
	std::ostringstream oss;
	oss << default_value;

	std::string val = get_value(sSection, sKey, oss.str() );

	std::istringstream iss(val);
	int res = 0;
	iss >> res;
	if(iss.fail() )
	{
		// set default and return ..
		set_int(sSection, sKey, default_value);
		return default_value;
	}
 
	return res;
}


void IniFile::set_float( const std::string& sSection, 
			   const std::string& sKey, const float value, const char* comment  )
{
	std::ostringstream ss;
	ss.clear();
	ss << value;
 
	set_value(sSection, sKey, ss.str(), comment );
}


float IniFile::get_float(const std::string& sSection, 
			  const std::string& sKey, 
			  const float default_value)
{
	std::ostringstream oss;
	oss << default_value;


	std::string strval = get_value(sSection, sKey, oss.str() );

	std::istringstream iss(strval);
	float res = 0.0f;
	iss >> res;
	if(iss.fail() )
	{
		set_float(sSection, sKey, default_value);
		return default_value;
	}

	return res;
}

 


}

#pragma warning(pop)

